﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Diagnostics;
using Xeml.Document.CoreObjects;

namespace Xeml.Document.Manager
{
    public sealed class OnlineCacheManager
    {
        #region Singleton pattern
        static readonly OnlineCacheManager instance = new OnlineCacheManager();


        static OnlineCacheManager()
        {
        }

        OnlineCacheManager()
        {

            Init();
        }



        public static OnlineCacheManager Instance
        {
            get
            {
                return instance;
            }
        }
        #endregion

        private Dictionary<string, string> _cacheDict = new Dictionary<string, string>();
        private Dictionary<string, List<string>> _aliases = new Dictionary<string, List<string>>();

        public delegate void MessageEmittedEventHandler(object sender, MessageEventArgs e);
        public event MessageEmittedEventHandler MessageEmitted;
 
        public void StatusUpdate(string message)
        {
            if (MessageEmitted != null)
            {
                MessageEmitted(this, new MessageEventArgs(message, EventLogEntryType.Information));
            }
        } 

        public string DownloadContent(string uri)
        {
            if (string.IsNullOrEmpty(uri))
            {
                throw new ArgumentNullException("uri");
            }

            StatusUpdate("Downloading uri: " + uri);
            string ret = "";

            WebRequest wr = WebRequest.Create(uri);
            WebResponse wresp = null;
            if (!_cacheDict.ContainsKey(uri))
            {
                try
                {
                    wresp = wr.GetResponse();
                    Stream s = wresp.GetResponseStream();
                    StreamReader sr = new StreamReader(s);
                    ret = sr.ReadToEnd();

                    string fileName = Path.GetRandomFileName();
                    fileName = Path.Combine(_cachePath, fileName);
                    while (File.Exists(fileName))
                    {
                        fileName = Path.GetRandomFileName();
                        fileName = Path.Combine(_cachePath, fileName);
                    }
                    _cacheDict.Add(uri, fileName);
                    File.WriteAllText(fileName, ret);
                    WriteCache();
                    StatusUpdate("Added resource to cache: " + uri);
                }
                catch (WebException ex)
                {
                    throw new FileNotFoundException("Resource not found in cache!", ex);
                }
                finally
                {
                    if (wresp != null)
                    {
                        wresp.Close();
                    }
                }
            }
            else
            {
                StatusUpdate("Looking for cached version: " + uri);
                string reverseAlias = FindAliasRevers(uri);
                if (_cacheDict.ContainsKey(reverseAlias))
                {
                    StatusUpdate("Reading cached version: " + uri);
                    ret = File.ReadAllText(_cacheDict[reverseAlias]);
                }
                else
                {
                    throw new FileNotFoundException("Resource not found in cache!");
                }
            }

            return ret;
        }

        private string FindAliasRevers(string uri)
        {
            foreach (KeyValuePair<string, List<string>> kvp in _aliases)
            {
                if (kvp.Value.Contains(uri))
                {
                    return kvp.Key;
                }
            }
            return uri;
        }

        Dictionary<string, string> _runtimeCache = new Dictionary<string, string>();

        public string ResolveUri(string url)
        {
            StatusUpdate("Begin ResolveUri for: " + url);
            string ret;
            if (_runtimeCache.ContainsKey(url)) //check whether we already resoved this Uri during the lifetime of the Manager
            {
                ret = _runtimeCache[url];
            }
            else
            {
                System.Net.WebResponse wres = null;
                try
                {
                    System.Net.WebRequest wr = System.Net.WebRequest.Create(url);
                    wr.Timeout = 3000;
                    wres = wr.GetResponse();

                    ret = wres.ResponseUri.AbsoluteUri;
                    if (!_aliases.ContainsKey(ret))
                    {
                        List<string> a = new List<string>();
                        _aliases.Add(ret, a);
                        //WriteAliases();
                    }
                    List<string> als = _aliases[ret];
                    if (!als.Contains(url))
                    {
                        als.Add(url);
                    }
                    WriteAliases();
                    _runtimeCache.Add(url, ret);
                }
                catch
                {
                    ret = url;
                    _runtimeCache.Add(url, ret);
                }
                finally
                {
                    if (wres != null)
                    {
                        wres.Close();
                    }
                }
            }
            StatusUpdate("End ResolveUri with: " + url);
            return ret;
        }

        private string _cachePath;
        private string _cacheFile;
        private string _aliasFile;
        private void Init()
        {
            _cachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MPIMP\\XemlOnlineCache");
            if (!Directory.Exists(_cachePath))
                Directory.CreateDirectory(_cachePath);

            _cacheFile = Path.Combine(_cachePath, "cache.txt");
            _aliasFile = Path.Combine(_cachePath, "alias.txt");
            ReadCache();
            ReadAliases();
        }

        private void ReadAliases()
        {
            if (File.Exists(_aliasFile))
            {
                string[] lines = File.ReadAllLines(_aliasFile);
                foreach (string line in lines)
                {
                    string[] entries = line.Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
                    if (entries.Length > 1)
                    {
                        List<string> aliases = new List<string>();
                        if (_aliases.ContainsKey(entries[0]))
                            aliases = _aliases[entries[0]];
                        else
                            _aliases.Add(entries[0], aliases);

                        for (int i = 1; i < entries.Length; i++)
                        {
                            if (!aliases.Contains(entries[i]))
                                aliases.Add(entries[i]);
                        }

                    }
                }
            }
        }
        private void WriteAliases()
        {
            StringBuilder sb = new StringBuilder();
            foreach (KeyValuePair<string,List<string>> kvp in _aliases)
            {
                sb.AppendFormat("{0}\t",kvp.Key);
                foreach (string a in kvp.Value)
                {
                    sb.AppendFormat("{0}\t",a);
                }
                sb.Remove(sb.Length - 1, 1);
                sb.Append("\r\n");
            }
            File.WriteAllText(_aliasFile, sb.ToString());
        }

        private void ReadCache()
        {
            if (File.Exists(_cacheFile))
            {
                string[] lines = File.ReadAllLines(_cacheFile);
                foreach (string line in lines)
                {
                    string[] entry = line.Split("\t".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    if (entry.Length > 1)
                    {
                        _cacheDict.Add(entry[0], entry[1]);
                    }
                }
            }
        } 
        private void WriteCache()
        {
            StringBuilder sb = new StringBuilder();
            foreach (KeyValuePair<string, string> kvp in _cacheDict)
            {
                sb.AppendFormat("{0}\t{1}\r\n", kvp.Key, kvp.Value);
            }
            File.WriteAllText(_cacheFile, sb.ToString());
        }
    }
}
